/**
 * @private
 */
Ext.define('Ext.file.file.Android', {

    extend: 'Ext.file.file.Abstract',
    config: {

        PG: {}

    },
    constructor: function (config) {
        this.initConfig(config);
        /*
        * PhoneGap is available under *either* the terms of the modified BSD license *or* the
        * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
        *
        * Copyright (c) 2005-2010, Nitobi Software Inc.
        * Copyright (c) 2010-2011, IBM Corporation
        */

        /**
        * The order of events during page load and PhoneGap startup is as follows:
        *
        * onDOMContentLoaded         Internal event that is received when the web page is loaded and parsed.
        * window.onload              Body onload event.
        * onNativeReady              Internal event that indicates the PhoneGap native side is ready.
        * onPhoneGapInit             Internal event that kicks off creation of all PhoneGap JavaScript objects (runs constructors).
        * onPhoneGapReady            Internal event fired when all PhoneGap JavaScript objects have been created
        * onPhoneGapInfoReady        Internal event fired when device properties are available
        * onDeviceReady              User event fired to indicate that PhoneGap is ready
        * onResume                   User event fired to indicate a start/resume lifecycle event
        * onPause                    User event fired to indicate a pause lifecycle event
        * onDestroy                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
        *
        * The only PhoneGap events that user code should register for are:
        *      onDeviceReady
        *      onResume
        *
        * Listeners can be registered as:
        *      document.addEventListener("deviceready", myDeviceReadyListener, false);
        *      document.addEventListener("resume", myResumeListener, false);
        *      document.addEventListener("pause", myPauseListener, false);
        */

        if (typeof (DeviceInfo) !== 'object') {
            var DeviceInfo = {};
        }

        /**
        * This represents the PhoneGap API itself, and provides a global namespace for accessing
        * information about the state of PhoneGap.
        * @class
        */
        var PG = {
            queue: {
                ready: true,
                commands: [],
                timer: null
            }
        };

        // alias PG to PhoneGap for compatibilty with common JS
        var PhoneGap = PG;

        /**
        * Custom pub-sub channel that can have functions subscribed to it
        * @constructor
        */
        PG.Channel = function (type) {
            this.type = type;
            this.handlers = {};
            this.guid = 0;
            this.fired = false;
            this.enabled = true;
        };

        /**
        * Subscribes the given function to the channel. Any time that
        * Channel.fire is called so too will the function.
        * Optionally specify an execution context for the function
        * and a guid that can be used to stop subscribing to the channel.
        * Returns the guid.
        */
        PG.Channel.prototype = {
            subscribe: function (f, c, g) {
                // need a function to call
                if (f === null) return;

                var func = f;
                if (typeof c === "object" && typeof f === "function") { func = PG.close(c, f); }

                g = g || func.observer_guid || f.observer_guid || this.guid++;
                func.observer_guid = g;
                f.observer_guid = g;
                this.handlers[g] = func;
                return g;
            },

            /**
            * Like subscribe but the function is only called once and then it
            * auto-unsubscribes itself.
            */
            subscribeOnce: function (f, c) {
                var g = null;
                var _this = this;
                var m = function () {
                    f.apply(c || null, arguments);
                    _this.unsubscribe(g);
                };
                if (this.fired) {
                    if (typeof c === "object" && typeof f === "function") f = PG.close(c, f);
                    f.apply(this, this.fireArgs);
                } else {
                    g = this.subscribe(m);
                }
                return g;
            },

            /**
            * Unsubscribes the function with the given guid from the channel.
            */
            unsubscribe: function (g) {
                if (typeof g === "function") { g = g.observer_guid; }
                this.handlers[g] = null;
                delete this.handlers[g];
            },

            /**
            * Calls all functions subscribed to this channel.
            */
            fire: function (e) {
                if (this.enabled) {
                    var fail = false;
                    var item, handler, rv;
                    for (item in this.handlers) {
                        if (this.handlers.hasOwnProperty(item)) {
                            handler = this.handlers[item];
                            if (typeof handler === "function") {
                                rv = (handler.apply(this, arguments) === false);
                                fail = fail || rv;
                            }
                        }
                    }
                    this.fired = true;
                    this.fireArgs = arguments;
                    return !fail;
                }
                return true;
            }
        }

        /**
        * Calls the provided function only after all of the channels specified
        * have been fired.
        */
        PG.Channel.join = function (h, c) {
            var i = c.length, f = function () {
                if (!(--i)) h();
            }, len = i, j;
            for (j = 0; j < len; j++) {
                if (!c[j].fired) c[j].subscribeOnce(f);
                else i--;
            }
            if (!i) h();
        };

        /**
        * Boolean flag indicating if the PhoneGap API is available and initialized.
        */
        // TODO: Remove this, it is unused here ... -jm
        PG.available = DeviceInfo.uuid !== undefined;

        /**
        * onDOMContentLoaded channel is fired when the DOM content
        * of the page has been parsed.
        */
        PG.onDOMContentLoaded = new PG.Channel('onDOMContentLoaded');

        /**
        * onNativeReady channel is fired when the PhoneGap native code
        * has been initialized.
        */
        PG.onNativeReady = new PG.Channel('onNativeReady');

        /**
        * onPhoneGapInit channel is fired when the web page is fully loaded and
        * PhoneGap native code has been initialized.
        */
        PG.onPhoneGapInit = new PG.Channel('onPhoneGapInit');

        /**
        * onPhoneGapReady channel is fired when the JS PhoneGap objects have been created.
        */
        PG.onPhoneGapReady = new PG.Channel('onPhoneGapReady');

        /**
        * onPhoneGapInfoReady channel is fired when the PhoneGap device properties
        * has been set.
        */
        PG.onPhoneGapInfoReady = new PG.Channel('onPhoneGapInfoReady');

        /**
        * onPhoneGapConnectionReady channel is fired when the PhoneGap connection properties
        * has been set.
        */
        PG.onPhoneGapConnectionReady = new PG.Channel('onPhoneGapConnectionReady');

        /**
        * onResume channel is fired when the PG native code
        * resumes.
        */
        PG.onResume = new PG.Channel('onResume');

        /**
        * onPause channel is fired when the PhoneGap native code
        * pauses.
        */
        PG.onPause = new PG.Channel('onPause');

        /**
        * onDestroy channel is fired when the PhoneGap native code
        * is destroyed.  It is used internally.
        * Window.onunload should be used by the user.
        */
        PG.onDestroy = new PG.Channel('onDestroy');
        PG.onDestroy.subscribeOnce(function () {
            PG.shuttingDown = true;
        });
        PG.shuttingDown = false;

        // _nativeReady is global variable that the native side can set
        // to signify that the native code is ready. It is a global since
        // it may be called before any PhoneGap JS is ready.
        if (typeof _nativeReady !== 'undefined') { PG.onNativeReady.fire(); }

        /**
        * onDeviceReady is fired only after all PhoneGap objects are created and
        * the device properties are set.
        */
        PG.onDeviceReady = new PG.Channel('onDeviceReady');


        // Array of channels that must fire before "deviceready" is fired
        PG.deviceReadyChannelsArray = [PG.onPhoneGapReady, PG.onPhoneGapInfoReady, PG.onPhoneGapConnectionReady];

        // Hashtable of user defined channels that must also fire before "deviceready" is fired
        PG.deviceReadyChannelsMap = {};

        /**
        * Indicate that a feature needs to be initialized before it is ready to be used.
        * This holds up PhoneGap's "deviceready" event until the feature has been initialized
        * and PhoneGap.initComplete(feature) is called.
        *
        * @param feature {String}     The unique feature name
        */
        PG.waitForInitialization = function (feature) {
            if (feature) {
                var channel = new PG.Channel(feature);
                PG.deviceReadyChannelsMap[feature] = channel;
                PG.deviceReadyChannelsArray.push(channel);
            }
        };

        /**
        * Indicate that initialization code has completed and the feature is ready to be used.
        *
        * @param feature {String}     The unique feature name
        */
        PG.initializationComplete = function (feature) {
            var channel = PG.deviceReadyChannelsMap[feature];
            if (channel) channel.fire();
        };

        /**
        * Create all PhoneGap objects once page has fully loaded and native side is ready.
        */
        PG.Channel.join(function () {

            // Start listening for XHR callbacks
            setTimeout(function () {
                if (PG.usePolling) {
                    PG.pollCallbackServer();
                } else {
                    var polling = prompt("usePolling", "gap_callbackServer:");
                    PG.usePolling = polling;
                    if (polling == "true") {
                        PG.usePolling = true;
                        PG.pollCallbackServer();
                    } else {
                        PG.usePolling = false;
                        PG.requestCallbackServer();
                    }
                }
            }, 1);

            // Run PhoneGap constructors
            PG.onPhoneGapInit.fire();

            // Fire event to notify that all objects are created
            PG.onPhoneGapReady.fire();

            // Fire onDeviceReady event once all constructors have run and PhoneGap info has been
            // received from native side, and any user defined initialization channels.
            PG.Channel.join(function () {

                // Turn off app loading dialog
                //PG.notification.activityStop();

                PG.onDeviceReady.fire();

                // Fire the onresume event, since first one happens before JavaScript is loaded
                PG.onResume.fire();
            }, PG.deviceReadyChannelsArray);

        }, [PG.onDOMContentLoaded, PG.onNativeReady]);

        // Listen for DOMContentLoaded and notify our channel subscribers
        document.addEventListener('DOMContentLoaded', function () {
            PG.onDOMContentLoaded.fire();
        }, false);

        // Intercept calls to document.addEventListener and watch for deviceready
        PG.m_document_addEventListener = document.addEventListener;

        document.addEventListener = function (evt, handler, capture) {
            var e = evt.toLowerCase();
            if (e === 'deviceready') PG.onDeviceReady.subscribeOnce(handler);
            else if (e === 'resume') {
                PG.onResume.subscribe(handler);
                if (PG.onDeviceReady.fired) PG.onResume.fire();
            } else if (e === 'pause') PG.onPause.subscribe(handler);
            else {
                // If subscribing to Android backbutton
                if (e === 'backbutton') PG.exec(null, null, "App", "overrideBackbutton", [true]);
                PG.m_document_addEventListener.call(document, evt, handler, capture);
            }
        };

        // Intercept calls to document.removeEventListener and watch for events that
        // are generated by PhoneGap native code
        PG.m_document_removeEventListener = document.removeEventListener;

        document.removeEventListener = function (evt, handler, capture) {
            var e = evt.toLowerCase();

            // If unsubscribing to Android backbutton
            if (e === 'backbutton') PG.exec(null, null, "App", "overrideBackbutton", [false]);
            PG.m_document_removeEventListener.call(document, evt, handler, capture);
        };

        /**
        * Method to fire event from native code
        */
        PG.fireEvent = function (type) {
            var e = document.createEvent('Events');
            e.initEvent(type);
            document.dispatchEvent(e);
        };

        /**
        * If JSON not included, use our own stringify. (Android 1.6)
        * The restriction on ours is that it must be an array of simple types.
        *
        * @param args
        * @return {String}
        */
        PG.stringify = function (args) {
            if (typeof JSON === "undefined") {
                var s = "[";
                var i, type, start, name, nameType, a;
                for (i = 0; i < args.length; i++) {
                    if (args[i] !== null) {
                        if (i > 0) s = s + ",";
                        type = typeof args[i];
                        if ((type === "number") || (type === "boolean")) s = s + args[i];
                        else if (args[i] instanceof Array) s = s + "[" + args[i] + "]";
                        else if (args[i] instanceof Object) {
                            start = true;
                            s = s + '{';
                            for (name in args[i]) {
                                if (args[i][name] !== null) {
                                    if (!start) s = s + ',';
                                    s = s + '"' + name + '":';
                                    nameType = typeof args[i][name];
                                    if ((nameType === "number") || (nameType === "boolean")) s = s + args[i][name];
                                    else if ((typeof args[i][name]) === 'function') s = s + '""';
                                    else if (args[i][name] instanceof Object) s = s + PG.stringify(args[i][name]);
                                    else s = s + '"' + args[i][name] + '"';
                                    start = false;
                                }
                            }
                            s = s + '}';
                        } else {
                            a = args[i].replace(/\\/g, '\\\\');
                            a = a.replace(/"/g, '\\"');
                            s = s + '"' + a + '"';
                        }
                    }
                }
                s = s + "]";
                return s;
            } else {
                return JSON.stringify(args);
            }
        };

        /**
        * Does a deep clone of the object.
        *
        * @param obj
        * @return {Object}
        */
        PG.clone = function (obj) {
            var i, retVal;
            if (!obj) return obj;

            if (obj instanceof Array) {
                retVal = [];
                for (i = 0; i < obj.length; ++i) retVal.push(PG.clone(obj[i]));
                return retVal;
            }

            if (typeof obj === "function") return obj;

            if (!(obj instanceof Object)) return obj;

            if (obj instanceof Date) return obj;

            retVal = {};
            for (i in obj) {
                if (!(i in retVal) || retVal[i] !== obj[i]) retVal[i] = PG.clone(obj[i]);
            }
            return retVal;
        };

        PG.callbackId = 0;
        PG.callbacks = {};
        PG.callbackStatus = {
            NO_RESULT: 0,
            OK: 1,
            CLASS_NOT_FOUND_EXCEPTION: 2,
            ILLEGAL_ACCESS_EXCEPTION: 3,
            INSTANTIATION_EXCEPTION: 4,
            MALFORMED_URL_EXCEPTION: 5,
            IO_EXCEPTION: 6,
            INVALID_ACTION: 7,
            JSON_EXCEPTION: 8,
            ERROR: 9
        };


        /**
        * Execute a PhoneGap command.  It is up to the native side whether this action is synch or async.
        * The native side can return:
        *      Synchronous: PluginResult object as a JSON string
        *      Asynchrounous: Empty string ""
        * If async, the native side will PhoneGap.callbackSuccess or PhoneGap.callbackError,
        * depending upon the result of the action.
        *
        * @param {Function} success    The success callback
        * @param {Function} fail       The fail callback
        * @param {String} service      The name of the service to use
        * @param {String} action       Action to be run in PhoneGap
        * @param {Array.<String>} [args]     Zero or more arguments to pass to the method
        */
        PG.exec = function (success, fail, service, action, args) {
            try {
                var callbackId = service + PG.callbackId++;
                if (success || fail) PG.callbacks[callbackId] = { success: success, fail: fail };

                var r = prompt(PG.stringify(args), "gap:" + PG.stringify([service, action, callbackId, true]));

                // If a result was returned
                if (r.length > 0) {
                    eval("var v=" + r + ";");

                    // If status is OK, then return value back to caller
                    if (v.status === PG.callbackStatus.OK) {

                        // If there is a success callback, then call it now with
                        // returned value
                        if (success) {
                            try {
                                success(v.message);
                            } catch (e) {
                                console.log("Error in success callback: " + callbackId + " = " + e);
                            }

                            // Clear callback if not expecting any more results
                            if (!v.keepCallback) {
                                delete PG.callbacks[callbackId];
                            }
                        }
                        return v.message;
                    } else if (v.status === PG.callbackStatus.NO_RESULT) { // If no result

                        // Clear callback if not expecting any more results
                        if (!v.keepCallback) delete PG.callbacks[callbackId];
                    } else { // If error, then display error
                        console.log("Error: Status=" + v.status + " Message=" + v.message);

                        // If there is a fail callback, then call it now with returned value
                        if (fail) {
                            try {
                                fail(v.message);
                            } catch (e1) {
                                console.log("Error in error callback: " + callbackId + " = " + e1);
                            }

                            // Clear callback if not expecting any more results
                            if (!v.keepCallback) delete PG.callbacks[callbackId];
                        }
                        return null;
                    }
                }
            } catch (e2) {
                console.log("Error: " + e2);
            }
        };

        /**
        * Called by native code when returning successful result from an action.
        *
        * @param callbackId
        * @param args
        */
        PG.callbackSuccess = function (callbackId, args) {
            if (PG.callbacks[callbackId]) {

                // If result is to be sent to callback
                if (args.status === PG.callbackStatus.OK) {
                    try {
                        if (PG.callbacks[callbackId].success) PG.callbacks[callbackId].success(args.message);
                    } catch (e) {
                        console.log("Error in success callback: " + callbackId + " = " + e);
                    }
                }

                // Clear callback if not expecting any more results
                if (!args.keepCallback) delete PG.callbacks[callbackId];
            }
        };

        /**
        * Called by native code when returning error result from an action.
        *
        * @param callbackId
        * @param args
        */
        PG.callbackError = function (callbackId, args) {
            if (PG.callbacks[callbackId]) {
                try {
                    if (PG.callbacks[callbackId].fail) PG.callbacks[callbackId].fail(args.message);
                } catch (e) {
                    console.log("Error in error callback: " + callbackId + " = " + e);
                }

                // Clear callback if not expecting any more results
                if (!args.keepCallback) delete PG.callbacks[callbackId];
            }
        };

        PG.callbackServerPort = null;
        PG.callbackServerToken = null;

        /**
        * This is only for Android.
        *
        * Internal function that uses XHR to call into PhoneGap Java code and retrieve
        * any JavaScript code that needs to be run.  This is used for callbacks from
        * Java to JavaScript.
        */
        PG.requestCallbackServer = function () {

            // Exit if shutting down app
            if (PG.shuttingDown) return;

            // If polling flag was changed, start using polling from now on
            if (PG.usePolling) {
                PG.pollCallbackServer();
                return;
            }

            var xmlhttp = new XMLHttpRequest();

            // Callback function when XMLHttpRequest is ready
            xmlhttp.onreadystatechange = function () {
                if (xmlhttp.readyState === 4) {

                    // Exit if shutting down app
                    if (PG.shuttingDown) return;

                    // If callback has JavaScript statement to execute
                    if (xmlhttp.status === 200) {

                        // Need to url decode the response
                        var msg = decodeURIComponent(xmlhttp.responseText);
                        setTimeout(function () {
                            try {
                                var t = eval(msg);
                            } catch (e) {
                                // If we're getting an error here, seeing the message will help in debugging
                                console.log("requestCallbackServer: Message from Server: " + msg);
                                console.log("requestCallbackServer Error: " + e);
                            }
                        }, 1);
                        setTimeout(PG.requestCallbackServer, 1);
                    } else if (xmlhttp.status === 404) {    // If callback ping (used to keep XHR request from timing out)
                        setTimeout(PG.requestCallbackServer, 10);
                    } else if (xmlhttp.status === 403) {    // If security error
                        console.log("requestCallbackServer Error: Invalid token.  Stopping callbacks.");
                    } else if (xmlhttp.status === 503) {    // If server is stopping
                        console.log("requestCallbackServer Error: Service unavailable.  Stopping callbacks.");
                    } else if (xmlhttp.status === 400) {    // If request wasn't GET
                        console.log("requestCallbackServer Error: Bad request.  Stopping callbacks.");
                    } else {                                // If error, revert to polling
                        console.log("requestCallbackServer Error: Request failed.");
                        PG.usePolling = true;
                        PG.pollCallbackServer();
                    }
                }
            };

            if (PG.callbackServerPort === null) PG.callbackServerPort = prompt("getPort", "gap_callbackServer:");
            if (PG.callbackServerToken === null) PG.callbackServerToken = prompt("getToken", "gap_callbackServer:");

            xmlhttp.open("GET", "http://127.0.0.1:" + PG.callbackServerPort + "/" + PG.callbackServerToken, true);
            xmlhttp.send();
        };

        /**
        * The polling period to use with pollCallbackServer.
        * This can be changed by the application.  The default is 50ms.
        */
        PG.pollCallbackServerPeriod = 50;

        /**
        * Flag that can be set by the user to force polling to be used or force XHR to be used.
        */
        PG.usePolling = false;    // T=use polling, F=use XHR

        /**
        * This is only for Android.
        *
        * Internal function that uses polling to call into PhoneGap Java code and retrieve
        * any JavaScript code that needs to be run.  This is used for callbacks from
        * Java to JavaScript.
        */
        PG.pollCallbackServer = function () {

            // Exit if shutting down app
            if (PG.shuttingDown) return;

            // If polling flag was changed, stop using polling from now on
            if (!PG.usePolling) {
                PG.requestCallbackServer();
                return;
            }

            var msg = prompt("", "gap_poll:");
            if (msg) {
                setTimeout(function () {
                    try {
                        var t = eval("" + msg);
                    } catch (e) {
                        console.log("pollCallbackServer: Message from Server: " + msg);
                        console.log("pollCallbackServer Error: " + e);
                    }
                }, 1);
                setTimeout(PG.pollCallbackServer, 1);
            } else {
                setTimeout(PG.pollCallbackServer, PG.pollCallbackServerPeriod);
            }
        };

        /**
        * Create a UUID
        *
        * @return {String}
        */
        PG.createUUID = function () {
            return PG.UUIDcreatePart(4) + '-' +
        PG.UUIDcreatePart(2) + '-' +
        PG.UUIDcreatePart(2) + '-' +
        PG.UUIDcreatePart(2) + '-' +
        PG.UUIDcreatePart(6);
        };

        PG.UUIDcreatePart = function (length) {
            var uuidpart = "";
            var i, uuidchar;
            for (i = 0; i < length; i++) {
                uuidchar = parseInt((Math.random() * 256), 0).toString(16);
                if (uuidchar.length === 1) uuidchar = "0" + uuidchar;
                uuidpart += uuidchar;
            }
            return uuidpart;
        };

        PG.close = PG.bind = function (context, func, params) {
            if (typeof params === 'undefined') return function () {
                return func.apply(context, arguments);
            };
            else return function () {
                return func.apply(context, params);
            };
        };
        /**
        * Constructor
        * @constructor
        */
        PG.app = {

            /**
            * Clear the resource cache.
            */
            clearCache: function () {
                PG.exec(null, null, "App", "clearCache", []);
            },

            /**
            * Load the url into the webview.
            *
            * @param url           The URL to load
            * @param props         Properties that can be passed in to the activity:
            *      wait: int                           => wait msec before loading URL
            *      loadingDialog: "Title,Message"      => display a native loading dialog
            *      hideLoadingDialogOnPage: boolean    => hide loadingDialog when page loaded instead of when deviceready event occurs.
            *      loadInWebView: boolean              => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser.
            *      loadUrlTimeoutValue: int            => time in msec to wait before triggering a timeout error
            *      errorUrl: URL                       => URL to load if there's an error loading specified URL with loadUrl().  Should be a local URL such as file:///android_asset/www/error.html");
            *      keepRunning: boolean                => enable app to keep running in background
            *
            * Example:
            *      App app = new App();
            *      app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
            */
            loadUrl: function (url, props) {
                PG.exec(null, null, "App", "loadUrl", [url, props]);
            },

            /**
            * Cancel loadUrl that is waiting to be loaded.
            */
            cancelLoadUrl: function () {
                PG.exec(null, null, "App", "cancelLoadUrl", []);
            },

            /**
            * Clear web history in this web view.
            * Instead of BACK button loading the previous web page, it will exit the app.
            */
            clearHistory: function () {
                PG.exec(null, null, "App", "clearHistory", []);
            },

            /**
            * Override the default behavior of the Android back button.
            * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
            *
            * Note: The user should not have to call this method.  Instead, when the user
            *       registers for the "backbutton" event, this is automatically done.
            *
            * @param override		T=override, F=cancel override
            */
            overrideBackbutton: function (override) {
                PG.exec(null, null, "App", "overrideBackbutton", [override]);
            },

            /**
            * Exit and terminate the application.
            */
            exitApp: function () {
                return PG.exec(null, null, "App", "exitApp", []);
            }
        };
        /*
        * PhoneGap is available under *either* the terms of the modified BSD license *or* the
        * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
        *
        * Copyright (c) 2005-2010, Nitobi Software Inc.
        * Copyright (c) 2010-2011, IBM Corporation
        */

        /**
        * This class provides some useful information about a file.
        * This is the fields returned when PG.fileMgr.getFileProperties()
        * is called.
        * @constructor
        */
        var FileProperties = function (filePath) {
            this.filePath = filePath;
            this.size = 0;
            this.lastModifiedDate = null;
        };

        /**
        * Represents a single file.
        *
        * @constructor
        * @param name {DOMString} name of the file, without path information
        * @param fullPath {DOMString} the full path of the file, including the name
        * @param type {DOMString} mime type
        * @param lastModifiedDate {Date} last modified date
        * @param size {Number} size of the file in bytes
        */
        var File = function (name, fullPath, type, lastModifiedDate, size) {
            this.name = name || null;
            this.fullPath = fullPath || null;
            this.type = type || null;
            this.lastModifiedDate = lastModifiedDate || null;
            this.size = size || 0;
        };

        /** @constructor */
        var FileError = function () {
            this.code = null;
        };

        // File error codes
        // Found in DOMException
        FileError.NOT_FOUND_ERR = 1;
        FileError.SECURITY_ERR = 2;
        FileError.ABORT_ERR = 3;

        // Added by this specification
        FileError.NOT_READABLE_ERR = 4;
        FileError.ENCODING_ERR = 5;
        FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
        FileError.INVALID_STATE_ERR = 7;
        FileError.SYNTAX_ERR = 8;
        FileError.INVALID_MODIFICATION_ERR = 9;
        FileError.QUOTA_EXCEEDED_ERR = 10;
        FileError.TYPE_MISMATCH_ERR = 11;
        FileError.PATH_EXISTS_ERR = 12;

        //-----------------------------------------------------------------------------
        // File manager
        //-----------------------------------------------------------------------------

        /** @constructor */
        PG.fileMgr = {
            getFileProperties: function (filePath) {
                return PG.exec(null, null, "File", "getFileProperties", [filePath]);
            },
            getFileBasePaths: function () {
            },
            testSaveLocationExists: function (successCallback, errorCallback) {
                return PG.exec(successCallback, errorCallback, "File", "testSaveLocationExists", []);
            },
            testFileExists: function (fileName, successCallback, errorCallback) {
                return PG.exec(successCallback, errorCallback, "File", "testFileExists", [fileName]);
            },
            testDirectoryExists: function (dirName, successCallback, errorCallback) {
                return PG.exec(successCallback, errorCallback, "File", "testDirectoryExists", [dirName]);
            },
            getFreeDiskSpace: function (successCallback, errorCallback) {
                return PG.exec(successCallback, errorCallback, "File", "getFreeDiskSpace", []);
            },
            writeAsText: function (fileName, data, append, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "writeAsText", [fileName, data, append]);
            },
            write: function (fileName, data, position, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "write", [fileName, data, position]);
            },
            truncate: function (fileName, size, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "truncate", [fileName, size]);
            },
            readAsText: function (fileName, encoding, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "readAsText", [fileName, encoding]);
            },
            readAsDataURL: function (fileName, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "readAsDataURL", [fileName]);
            },
            fire: function (obj, evt, params) {
                if (typeof obj[evt] === "function") obj[evt](params);
            }
        }

        //-----------------------------------------------------------------------------
        // File Reader
        //-----------------------------------------------------------------------------
        // TODO: All other FileMgr function operate on the SD card as root.  However,
        //       for FileReader & FileWriter the root is not SD card.  Should this be changed?

        /**
        * This class reads the mobile device file system.
        *
        * For Android:
        *      The root directory is the root of the file system.
        *      To read from the SD card, the file name is "sdcard/my_file.txt"
        * @constructor
        */
        var FileReader = function () {
            this.fileName = "";

            this.readyState = 0;

            // File data
            this.result = null;

            // Error
            this.error = null;

            // Event handlers
            this.onloadstart = null;    // When the read starts.
            this.onprogress = null;     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progess.loaded/progress.total)
            this.onload = null;         // When the read has successfully completed.
            this.onerror = null;        // When the read has failed (see errors).
            this.onloadend = null;      // When the request has completed (either in success or failure).
            this.onabort = null;        // When the read has been aborted. For instance, by invoking the abort() method.
        };

        // States
        FileReader.EMPTY = 0;
        FileReader.LOADING = 1;
        FileReader.DONE = 2;

        /**
        * Abort reading file.
        */
        FileReader.prototype = {
            abort: function () {
                var evt;
                this.readyState = FileReader.DONE;
                this.result = null;

                // set error
                var error = new FileError();
                error.code = error.ABORT_ERR;
                this.error = error;

                PG.fileMgr.fire(this, 'onerror', { "type": "error", "target": this });
                PG.fileMgr.fire(this, 'oneabort', { "type": "abort", "target": this });
                PG.fileMgr.fire(this, 'onloadend', { "type": "loadend", "target": this });
            },

            /**
            * Read text file.
            *
            * @param file          {File} File object containing file properties
            * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
            */
            readAsText: function (file, encoding) {
                this.fileName = file.fullPath || file;

                // LOADING state
                this.readyState = FileReader.LOADING;

                PG.fileMgr.fire(this, 'onloadstart', { "type": "loadstart", "target": this });

                // Default encoding is UTF-8
                var enc = encoding ? encoding : "UTF-8";

                var me = this;

                // Read file
                PG.fileMgr.readAsText(this.fileName, enc,

                // Success callback
            function (r) {
                var evt;

                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileReader.DONE) return;

                // Save result
                me.result = r;

                PG.fileMgr.fire(me, 'onload', { "type": "load", "target": me });

                // DONE state
                me.readyState = FileReader.DONE;

                // If onloadend callback
                PG.fileMgr.fire(me, 'onloadend', { "type": "loadend", "target": me });
            },

                // Error callback
            function (e) {
                var evt;
                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileReader.DONE) return;

                // Save error
                me.error = e;

                // If onerror callback
                PG.fileMgr.fire(me, 'onerror', { "type": "error", "target": me });

                // DONE state
                me.readyState = FileReader.DONE;

                // If onloadend callback
                PG.fileMgr.fire(me, 'onloadend', { "type": "loadend", "target": me });
            }
        );
            },


            /**
            * Read file and return data as a base64 encoded data url.
            * A data url is of the form:
            *      data:[<mediatype>][;base64],<data>
            *
            * @param file          {File} File object containing file properties
            */
            readAsDataURL: function (file) {
                this.fileName = file.fullPath || file;

                // LOADING state
                this.readyState = FileReader.LOADING;

                // If loadstart callback
                PG.fileMgr.fire(this, 'onloadstart', { "type": "loadstart", "target": this });

                var me = this;

                // Read file
                PG.fileMgr.readAsDataURL(this.fileName,

                // Success callback
            function (r) {
                var evt;

                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileReader.DONE) return;

                // Save result
                me.result = r;

                // If onload callback
                PG.fileMgr.fire(me, 'onload', { "type": "load", "target": me });

                // DONE state
                me.readyState = FileReader.DONE;

                // If onloadend callback
                PG.fileMgr.fire(me, 'onloadend', { "type": "loadend", "target": me });
            },

                // Error callback
            function (e) {
                var evt;
                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileReader.DONE) return;

                // Save error
                me.error = e;

                // If onerror callback
                PG.fileMgr.fire(me, 'onerror', { "type": "error", "target": me });

                // DONE state
                me.readyState = FileReader.DONE;

                // If onloadend callback
                PG.fileMgr.fire(me, 'onloadend', ({ "type": "loadend", "target": me }));
            }
        );
            },

            /**
            * Read file and return data as a binary data.
            *
            * @param file          {File} File object containing file properties
            */
            readAsBinaryString: function (file) {
                // TODO - Can't return binary data to browser.
                this.fileName = file;
            },

            /**
            * Read file and return data as a binary data.
            *
            * @param file          {File} File object containing file properties
            */
            readAsArrayBuffer: function (file) {
                // TODO - Can't return binary data to browser.
                this.fileName = file;
            }
        }

        //-----------------------------------------------------------------------------
        // File Writer
        //-----------------------------------------------------------------------------

        /**
        * This class writes to the mobile device file system.
        *
        * For Android:
        *      The root directory is the root of the file system.
        *      To write to the SD card, the file name is "sdcard/my_file.txt"
        *
        * @constructor
        * @param file {File} File object containing file properties
        * @param append if true write to the end of the file, otherwise overwrite the file
        */
        var FileWriter = function (file) {
            this.fileName = "";
            this.length = 0;
            if (file) {
                this.fileName = file.fullPath || file;
                this.length = file.size || 0;
            }
            // default is to write at the beginning of the file
            this.position = 0;

            this.readyState = 0; // EMPTY

            this.result = null;

            // Error
            this.error = null;

            // Event handlers
            this.onwritestart = null; // When writing starts
            this.onprogress = null; 	// While writing the file, and reporting partial file data
            this.onwrite = null; 	// When the write has successfully completed.
            this.onwriteend = null; 	// When the request has completed (either in success or failure).
            this.onabort = null; 	// When the write has been aborted. For instance, by invoking the abort() method.
            this.onerror = null; 	// When the write has failed (see errors).
        };

        // States
        FileWriter.INIT = 0;
        FileWriter.WRITING = 1;
        FileWriter.DONE = 2;

        /**
        * Abort writing file.
        */
        FileWriter.prototype = {
            abort: function () {
                // check for invalid state
                if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) throw FileError.INVALID_STATE_ERR;

                // set error
                var error = new FileError(), evt;
                error.code = error.ABORT_ERR;
                this.error = error;

                PG.fileMgr.fire(this, 'onerror', { "type": "error", "target": this });
                PG.fileMgr.fire(this, 'oneabort', { "type": "abort", "target": this });

                this.readyState = FileWriter.DONE;

                // If write end callback
                PG.fileMgr.fire(this, 'onwriteend', { "type": "writeend", "target": this });
            },

            /**
            * Writes data to the file
            *
            * @param text to be written
            */
            write: function (text) {
                // Throw an exception if we are already writing a file
                if (this.readyState === FileWriter.WRITING) throw FileError.INVALID_STATE_ERR;

                // WRITING state
                this.readyState = FileWriter.WRITING;

                var me = this;

                // If onwritestart callback
                PG.fileMgr.fire(me, 'onwritestart', { "type": "writestart", "target": me });

                // Write file
                PG.fileMgr.write(this.fileName, text, this.position,

                // Success callback
            function (r) {
                var evt;
                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileWriter.DONE) return;

                // position always increases by bytes written because file would be extended
                me.position += r;
                // The length of the file is now where we are done writing.
                me.length = me.position;

                // If onwrite callback
                PG.fileMgr.fire(me, 'onwrite', { "type": "write", "target": me });

                // DONE state
                me.readyState = FileWriter.DONE;

                // If onwriteend callback
                PG.fileMgr.fire(me, 'onwriteend', { "type": "writeend", "target": me });
            },

                // Error callback
            function (e) {
                var evt;

                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileWriter.DONE) return;

                // Save error
                me.error = e;

                // If onerror callback
                PG.fileMgr.fire(me, 'onerror', { "type": "error", "target": me });

                // DONE state
                me.readyState = FileWriter.DONE;

                // If onwriteend callback
                PG.fileMgr.fire(me, 'onwriteend', { "type": "writeend", "target": me });
            }
        );

            },

            /**
            * Moves the file pointer to the location specified.
            *
            * If the offset is a negative number the position of the file
            * pointer is rewound.  If the offset is greater than the file
            * size the position is set to the end of the file.
            *
            * @param offset is the location to move the file pointer to.
            */
            seek: function (offset) {
                // Throw an exception if we are already writing a file
                if (this.readyState === FileWriter.WRITING) throw FileError.INVALID_STATE_ERR;

                if (!offset) return;

                // See back from end of file.
                if (offset < 0) this.position = Math.max(offset + this.length, 0);
                // Offset is bigger then file size so set position
                // to the end of the file.
                else if (offset > this.length) this.position = this.length;
                // Offset is between 0 and file size so set the position
                // to start writing.
                else this.position = offset;
            },

            /**
            * Truncates the file to the size specified.
            *
            * @param size to chop the file at.
            */
            truncate: function (size) {
                // Throw an exception if we are already writing a file
                if (this.readyState === FileWriter.WRITING) throw FileError.INVALID_STATE_ERR;

                // WRITING state
                this.readyState = FileWriter.WRITING;

                var me = this;

                // If onwritestart callback
                PG.fileMgr.fire(me, 'onwritestart', { "type": "writestart", "target": this });

                // Write file
                PG.fileMgr.truncate(this.fileName, size,

                // Success callback
            function (r) {
                var evt;
                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileWriter.DONE) return;

                // Update the length of the file
                me.length = r;
                me.position = Math.min(me.position, r);

                // If onwrite callback
                PG.fileMgr.fire(me, 'onwrite', { "type": "write", "target": me });

                // DONE state
                me.readyState = FileWriter.DONE;

                // If onwriteend callback
                PG.fileMgr.fire(me, 'onwriteend', { "type": "writeend", "target": me });
            },

                // Error callback
            function (e) {
                var evt;
                // If DONE (cancelled), then don't do anything
                if (me.readyState === FileWriter.DONE) {
                    return;
                }

                // Save error
                me.error = e;

                // If onerror callback
                PG.fileMgr.fire(me, 'onerror', { "type": "error", "target": me });

                // DONE state
                me.readyState = FileWriter.DONE;

                // If onwriteend callback
                PG.fileMgr.fire(me, 'onwriteend', { "type": "writeend", "target": me });
            }
        );
            }
        }

        /**
        * Information about the state of the file or directory
        *
        * @constructor
        * {Date} modificationTime (readonly)
        */
        var Metadata = function () {
            this.modificationTime = null;
        };

        /**
        * Supplies arguments to methods that lookup or create files and directories
        *
        * @constructor
        * @param {boolean} create file or directory if it doesn't exist
        * @param {boolean} exclusive if true the command will fail if the file or directory exists
        */
        var Flags = function (create, exclusive) {
            this.create = create || false;
            this.exclusive = exclusive || false;
        };

        /**
        * An interface representing a file system
        *
        * @constructor
        * {DOMString} name the unique name of the file system (readonly)
        * {DirectoryEntry} root directory of the file system (readonly)
        */
        var FileSystem = function () {
            this.name = null;
            this.root = null;
        };

        /**
        * An interface that lists the files and directories in a directory.
        * @constructor
        */
        var DirectoryReader = function (fullPath) {
            this.fullPath = fullPath || null;
        };

        /**
        * Returns a list of entries from a directory.
        *
        * @param {Function} successCallback is called with a list of entries
        * @param {Function} errorCallback is called with a FileError
        */
        DirectoryReader.prototype.readEntries = function (successCallback, errorCallback) {
            PG.exec(successCallback, errorCallback, "File", "readEntries", [this.fullPath]);
        };

        /**
        * An interface representing a directory on the file system.
        *
        * @constructor
        * {boolean} isFile always false (readonly)
        * {boolean} isDirectory always true (readonly)
        * {DOMString} name of the directory, excluding the path leading to it (readonly)
        * {DOMString} fullPath the absolute full path to the directory (readonly)
        * {FileSystem} filesystem on which the directory resides (readonly)
        */
        var DirectoryEntry = function () {
            this.isFile = false;
            this.isDirectory = true;
            this.name = null;
            this.fullPath = null;
            this.filesystem = null;
        };

        DirectoryEntry.prototype = {

            /**
            * Copies a directory to a new location
            *
            * @param {DirectoryEntry} parent the directory to which to copy the entry
            * @param {DOMString} newName the new name of the entry, defaults to the current name
            * @param {Function} successCallback is called with the new entry
            * @param {Function} errorCallback is called with a FileError
            */
            copyTo: function (parent, newName, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]);
            },

            /**
            * Looks up the metadata of the entry
            *
            * @param {Function} successCallback is called with a Metadata object
            * @param {Function} errorCallback is called with a FileError
            */
            getMetadata: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]);
            },

            /**
            * Gets the parent of the entry
            *
            * @param {Function} successCallback is called with a parent entry
            * @param {Function} errorCallback is called with a FileError
            */
            getParent: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]);
            },

            /**
            * Moves a directory to a new location
            *
            * @param {DirectoryEntry} parent the directory to which to move the entry
            * @param {DOMString} newName the new name of the entry, defaults to the current name
            * @param {Function} successCallback is called with the new entry
            * @param {Function} errorCallback is called with a FileError
            */
            moveTo: function (parent, newName, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]);
            },

            /**
            * Removes the entry
            *
            * @param {Function} successCallback is called with no parameters
            * @param {Function} errorCallback is called with a FileError
            */
            remove: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]);
            },

            /**
            * Returns a URI that can be used to identify this entry.
            *
            * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
            * @return uri
            */
            toURI: function (mimeType) {
                return "file://" + this.fullPath;
            },

            /**
            * Creates a new DirectoryReader to read entries from this directory
            */
            createReader: function (successCallback, errorCallback) {
                return new DirectoryReader(this.fullPath);
            },

            /**
            * Creates or looks up a directory
            *
            * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
            * @param {Flags} options to create or excluively create the directory
            * @param {Function} successCallback is called with the new entry
            * @param {Function} errorCallback is called with a FileError
            */
            getDirectory: function (path, options, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "getDirectory", [this.fullPath, path, options]);
            },

            /**
            * Creates or looks up a file
            *
            * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
            * @param {Flags} options to create or excluively create the file
            * @param {Function} successCallback is called with the new entry
            * @param {Function} errorCallback is called with a FileError
            */
            getFile: function (path, options, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "getFile", [this.fullPath, path, options]);
            },

            /**
            * Deletes a directory and all of it's contents
            *
            * @param {Function} successCallback is called with no parameters
            * @param {Function} errorCallback is called with a FileError
            */
            removeRecursively: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "removeRecursively", [this.fullPath]);
            }
        };

        /**
        * An interface representing a directory on the file system.
        *
        * @constructor
        * {boolean} isFile always true (readonly)
        * {boolean} isDirectory always false (readonly)
        * {DOMString} name of the file, excluding the path leading to it (readonly)
        * {DOMString} fullPath the absolute full path to the file (readonly)
        * {FileSystem} filesystem on which the directory resides (readonly)
        */
        var FileEntry = function () {
            this.isFile = true;
            this.isDirectory = false;
            this.name = null;
            this.fullPath = null;
            this.filesystem = null;
        };

        FileEntry.prototype = {

            /**
            * Copies a file to a new location
            *
            * @param {DirectoryEntry} parent the directory to which to copy the entry
            * @param {DOMString} newName the new name of the entry, defaults to the current name
            * @param {Function} successCallback is called with the new entry
            * @param {Function} errorCallback is called with a FileError
            */
            copyTo: function (parent, newName, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]);
            },

            /**
            * Looks up the metadata of the entry
            *
            * @param {Function} successCallback is called with a Metadata object
            * @param {Function} errorCallback is called with a FileError
            */
            getMetadata: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]);
            },

            /**
            * Gets the parent of the entry
            *
            * @param {Function} successCallback is called with a parent entry
            * @param {Function} errorCallback is called with a FileError
            */
            getParent: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]);
            },

            /**
            * Moves a directory to a new location
            *
            * @param {DirectoryEntry} parent the directory to which to move the entry
            * @param {DOMString} newName the new name of the entry, defaults to the current name
            * @param {Function} successCallback is called with the new entry
            * @param {Function} errorCallback is called with a FileError
            */
            moveTo: function (parent, newName, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]);
            },

            /**
            * Removes the entry
            *
            * @param {Function} successCallback is called with no parameters
            * @param {Function} errorCallback is called with a FileError
            */
            remove: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]);
            },

            /**
            * Returns a URI that can be used to identify this entry.
            *
            * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
            * @return uri
            */
            toURI: function (mimeType) {
                return "file://" + this.fullPath;
            },

            /**
            * Creates a new FileWriter associated with the file that this FileEntry represents.
            *
            * @param {Function} successCallback is called with the new FileWriter
            * @param {Function} errorCallback is called with a FileError
            */
            createWriter: function (successCallback, errorCallback) {
                this.file(function (filePointer) {
                    var writer = new FileWriter(filePointer);

                    if (writer.fileName === null || writer.fileName === "") {
                        if (typeof errorCallback == "function") errorCallback({ "code": FileError.INVALID_STATE_ERR });
                    }
                    if (typeof successCallback == "function") successCallback(writer);
                }, errorCallback);
            },

            /**
            * Returns a File that represents the current state of the file that this FileEntry represents.
            *
            * @param {Function} successCallback is called with the new File object
            * @param {Function} errorCallback is called with a FileError
            */
            file: function (successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "getFileMetadata", [this.fullPath]);
            }
        };

        /** @constructor */
        var LocalFileSystem = function () {
        };

        // File error codes
        LocalFileSystem.TEMPORARY = 0;
        LocalFileSystem.PERSISTENT = 1;
        LocalFileSystem.RESOURCE = 2;
        LocalFileSystem.APPLICATION = 3;

        LocalFileSystem.prototype = {
            /**
            * Requests a filesystem in which to store application data.
            *
            * @param {int} type of file system being requested
            * @param {Function} successCallback is called with the new FileSystem
            * @param {Function} errorCallback is called with a FileError
            */
            requestFileSystem: function (type, size, successCallback, errorCallback) {
                if (type < 0 || type > 3) {
                    if (typeof errorCallback == "function") {
                        errorCallback({
                            "code": FileError.SYNTAX_ERR
                        });
                    }
                }
                else {
                    PG.exec(successCallback, errorCallback, "File", "requestFileSystem", [type, size]);
                }
            },

            /**
            *
            * @param {DOMString} uri referring to a local file in a filesystem
            * @param {Function} successCallback is called with the new entry
            * @param {Function} errorCallback is called with a FileError
            */
            resolveLocalFileSystemURI: function (uri, successCallback, errorCallback) {
                PG.exec(successCallback, errorCallback, "File", "resolveLocalFileSystemURI", [uri]);
            },

            /**
            * This function returns and array of contacts.  It is required as we need to convert raw
            * JSON objects into concrete Contact objects.  Currently this method is called after
            * navigator.service.contacts.find but before the find methods success call back.
            *
            * @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects.
            * @returns an entry
            */
            _castFS: function (pluginResult) {
                var entry = null;
                entry = new DirectoryEntry();
                entry.isDirectory = pluginResult.message.root.isDirectory;
                entry.isFile = pluginResult.message.root.isFile;
                entry.name = pluginResult.message.root.name;
                entry.fullPath = pluginResult.message.root.fullPath;
                pluginResult.message.root = entry;
                return pluginResult;
            },

            _castEntry: function (pluginResult) {
                var entry = null;
                if (pluginResult.message.isDirectory) {
                    console.log("This is a dir");
                    entry = new DirectoryEntry();
                }
                else if (pluginResult.message.isFile) {
                    console.log("This is a file");
                    entry = new FileEntry();
                }
                entry.isDirectory = pluginResult.message.isDirectory;
                entry.isFile = pluginResult.message.isFile;
                entry.name = pluginResult.message.name;
                entry.fullPath = pluginResult.message.fullPath;
                pluginResult.message = entry;
                return pluginResult;
            },

            _castEntries: function (pluginResult) {
                var entries = pluginResult.message;
                var retVal = [];
                for (var i = 0; i < entries.length; i++) {
                    retVal.push(window.localFileSystem._createEntry(entries[i]));
                }
                pluginResult.message = retVal;
                return pluginResult;
            },

            _createEntry: function (castMe) {
                var entry = null;
                if (castMe.isDirectory) {
                    console.log("This is a dir");
                    entry = new DirectoryEntry();
                }
                else if (castMe.isFile) {
                    console.log("This is a file");
                    entry = new FileEntry();
                }
                entry.isDirectory = castMe.isDirectory;
                entry.isFile = castMe.isFile;
                entry.name = castMe.name;
                entry.fullPath = castMe.fullPath;
                return entry;
            },

            _castDate: function (pluginResult) {
                if (pluginResult.message.modificationTime) {
                    var modTime = new Date(pluginResult.message.modificationTime);
                    pluginResult.message.modificationTime = modTime;
                }
                else if (pluginResult.message.lastModifiedDate) {
                    var file = new File();
                    file.size = pluginResult.message.size;
                    file.type = pluginResult.message.type;
                    file.name = pluginResult.message.name;
                    file.fullPath = pluginResult.message.fullPath;
                    file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate);
                    pluginResult.message = file;
                }
                return pluginResult;
            }
        };

        /**
        * Add the FileSystem interface into the browser.
        */
        /*
        PG.addConstructor(function() {
        var pgLocalFileSystem = new LocalFileSystem();
        // Needed for cast methods
        if(typeof window.localFileSystem == "undefined") window.localFileSystem  = pgLocalFileSystem;
        if(typeof window.requestFileSystem == "undefined") window.requestFileSystem  = pgLocalFileSystem.requestFileSystem;
        if(typeof window.resolveLocalFileSystemURI == "undefined") window.resolveLocalFileSystemURI = pgLocalFileSystem.resolveLocalFileSystemURI;
        });
        */
        if (typeof window.localFileSystem == "undefined") window.localFileSystem = pgLocalFileSystem;
        if (typeof window.requestFileSystem == "undefined") window.requestFileSystem = pgLocalFileSystem.requestFileSystem;
        if (typeof window.resolveLocalFileSystemURI == "undefined") window.resolveLocalFileSystemURI = pgLocalFileSystem.resolveLocalFileSystemURI;
        /*
        * PhoneGap is available under *either* the terms of the modified BSD license *or* the
        * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
        *
        * Copyright (c) 2005-2010, Nitobi Software Inc.
        * Copyright (c) 2010-2011, IBM Corporation
        */

        /**
        * FileTransfer uploads a file to a remote server.
        * @constructor
        */
        var FileTransfer = function () { };

        /**
        * FileUploadResult
        * @constructor
        */
        var FileUploadResult = function () {
            this.bytesSent = 0;
            this.responseCode = null;
            this.response = null;
        };

        /**
        * FileTransferError
        * @constructor
        */
        var FileTransferError = function () {
            this.code = null;
        };

        FileTransferError.FILE_NOT_FOUND_ERR = 1;
        FileTransferError.INVALID_URL_ERR = 2;
        FileTransferError.CONNECTION_ERR = 3;

        /**
        * Given an absolute file path, uploads a file on the device to a remote server
        * using a multipart HTTP request.
        * @param filePath {String}           Full path of the file on the device
        * @param server {String}             URL of the server to receive the file
        * @param successCallback (Function}  Callback to be invoked when upload has completed
        * @param errorCallback {Function}    Callback to be invoked upon error
        * @param options {FileUploadOptions} Optional parameters such as file name and mimetype
        */
        FileTransfer.prototype.upload = function (filePath, server, successCallback, errorCallback, options, debug) {

            // check for options
            var fileKey = null;
            var fileName = null;
            var mimeType = null;
            var params = null;
            if (options) {
                fileKey = options.fileKey;
                fileName = options.fileName;
                mimeType = options.mimeType;
                if (options.params) {
                    params = options.params;
                }
                else {
                    params = {};
                }
            }

            PG.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug]);
        };

        /**
        * Options to customize the HTTP request used to upload files.
        * @constructor
        * @param fileKey {String}   Name of file request parameter.
        * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
        * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
        * @param params {Object}    Object with key: value params to send to the server.
        */
        var FileUploadOptions = function (fileKey, fileName, mimeType, params) {
            this.fileKey = fileKey || null;
            this.fileName = fileName || null;
            this.mimeType = mimeType || null;
            this.params = params || null;
        };
        /*
        * PhoneGap is available under *either* the terms of the modified BSD license *or* the
        * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
        *
        * Copyright (c) 2005-2010, Nitobi Software Inc.
        * Copyright (c) 2010-2011, IBM Corporation
        */

        /**
        * This class provides access to notifications on the device.
        * @constructor
        */
        PG.notification = {

            /**
            * Open a native alert dialog, with a customizable title and button text.
            *
            * @param {String} message              Message to print in the body of the alert
            * @param {Function} completeCallback   The callback that is called when user clicks on a button.
            * @param {String} title                Title of the alert dialog (default: Alert)
            * @param {String} buttonLabel          Label of the close button (default: OK)
            */
            alert: function (message, completeCallback, title, buttonLabel) {
                var _title = (title || "Alert");
                var _buttonLabel = (buttonLabel || "OK");
                PG.exec(completeCallback, null, "Notification", "alert", [message, _title, _buttonLabel]);
            },

            /**
            * Open a native confirm dialog, with a customizable title and button text.
            * The result that the user selects is returned to the result callback.
            *
            * @param {String} message              Message to print in the body of the alert
            * @param {Function} resultCallback     The callback that is called when user clicks on a button.
            * @param {String} title                Title of the alert dialog (default: Confirm)
            * @param {String} buttonLabels         Comma separated list of the labels of the buttons (default: 'OK,Cancel')
            */
            confirm: function (message, resultCallback, title, buttonLabels) {
                var _title = (title || "Confirm");
                var _buttonLabels = (buttonLabels || "OK,Cancel");
                PG.exec(resultCallback, null, "Notification", "confirm", [message, _title, _buttonLabels]);
            },

            /**
            * Start spinning the activity indicator on the statusbar
            */
            activityStart: function () {
                PG.exec(null, null, "Notification", "activityStart", ["Busy", "Please wait..."]);
            },

            /**
            * Stop spinning the activity indicator on the statusbar, if it's currently spinning
            */
            activityStop: function () {
                PG.exec(null, null, "Notification", "activityStop", []);
            },

            /**
            * Display a progress dialog with progress bar that goes from 0 to 100.
            *
            * @param {String} title        Title of the progress dialog.
            * @param {String} message      Message to display in the dialog.
            */
            progressStart: function (title, message) {
                PG.exec(null, null, "Notification", "progressStart", [title, message]);
            },

            /**
            * Set the progress dialog value.
            *
            * @param {Number} value         0-100
            */
            progressValue: function (value) {
                PG.exec(null, null, "Notification", "progressValue", [value]);
            },

            /**
            * Close the progress dialog.
            */
            progressStop: function () {
                PG.exec(null, null, "Notification", "progressStop", []);
            },

            /**
            * Causes the device to blink a status LED.
            *
            * @param {Integer} count       The number of blinks.
            * @param {String} colour       The colour of the light.
            */
            blink: function (count, colour) {
                // NOT IMPLEMENTED
            },

            /**
            * Causes the device to vibrate.
            *
            * @param {Integer} mills       The number of milliseconds to vibrate for.
            */
            vibrate: function (mills) {
                PG.exec(null, null, "Notification", "vibrate", [mills]);
            },

            /**
            * Causes the device to beep.
            * On Android, the default notification ringtone is played "count" times.
            *
            * @param {Integer} count       The number of beeps.
            */
            beep: function (count) {
                PG.exec(null, null, "Notification", "beep", [count]);
            }
        };
        this.PG = PG;

    },
    fail: function (evt) {
        var mensaje =
              'file.file.dowload fail:' +
              '' + evt.target.error.code + '';
        console.log(mensaje);
    },
    loadUrl: function (url, props) {
        this.PG.app.loadUrl(url, props);
    },
    exitApp: function () {
        this.PG.app.exitApp();
    },
    download: function (url, alias) {
        var remoteFile = url;
        var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/') + 1);

        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
            fileSystem.root.getFile(localFileName, { create: true, exclusive: false }, function (fileEntry) {
                var localPath = fileEntry.fullPath;
                if (device.platform === "Android" && localPath.indexOf("file://") === 0) {
                    localPath = localPath.substring(7);
                }
                var ft = new FileTransfer();
                ft.download(remoteFile,
                    localPath, function (entry) {
                        navigator.getCodeURL().setUrl(entry.fullPath);
                        var dwnldImg = document.getElementById("dwnldImg");
                        dwnldImg.href = 'page.html';
                        dwnldImg.style.visibility = "visible";
                        dwnldImg.style.display = "block";
                    }, this.fail);
            }, this.fail);
        }, this.fail);
    }
});
